﻿<html>
<head>
<meta charset="utf-8" />
<title>中国象棋</title>
<style>
body{
    margin:0px;
}
.full{
    width: 100%;
    height: 100%;    
}
</style>
<script>

/*
屏幕坐标系 
 ___ x
|
y

竖直方向画布坐标系
 ___ x
|
y

水平方向画布坐标系
y___
    |
    x


棋盘坐标系
            将
0  1  2  3  4  5  6  7  8    
1
2  炮
3     卒      
4    
5
6     兵
7  炮
8
9

"将帥士仕象相車馬炮砲兵卒"
*/

const COLOR_BLACK = 1;
const COLOR_RED = 2;

//棋子初始化数据
const PIECE_INIT_DATA = [
    ['将','帥', jiangCanMove, [4,0]],
    ['士','仕', shiCanMove, [3,0, 5,0]],
    ['象','相', xiangCanMove, [2,0, 6,0]],
    ['車','車', juCanMove, [0,0, 8,0]],
    ['馬','馬', maCanMove, [1,0, 7,0]],
    ['炮','砲', paoCanMove, [1,2, 7,2]],
    ['卒','兵', bingCanMove, [0,3, 2,3, 4,3, 6,3, 8,3]],
];

//添加棋子
function initOnePiece(state, color, x, y, text, func){
    let piece = {};
    piece.x = x;
    piece.y = y;
    piece.color = color;
    piece.text = text;
    piece.canMove = func;
    state.pieces[x][y] = piece;
}

//按初始数据添加棋子
function initPieceByData(state, data){
    let canMove = data[2];
    let coords = data[3];
    for (let i = 0; i < coords.length; i += 2) {
        let x = coords[i];
        let y = coords[i + 1];        
        initOnePiece(state, COLOR_BLACK, x, y, data[0], canMove);
        initOnePiece(state, COLOR_RED, 8 - x, 9 - y, data[1], canMove);
    }
}


//创建初始棋子
function initPieces(state){
    console.log("reset");
    state.pieces = {};
    state.step = COLOR_RED;
    state.undos = [];
    state.moving = undefined;
    state.closing = undefined;
    state.select = undefined;
    for (let x = 0; x <= 8; x++) {
        state.pieces[x] = {};
    }    
    for (let i = 0; i < PIECE_INIT_DATA.length; i++) {
        let data = PIECE_INIT_DATA[i];
        initPieceByData(state, data);
    }
}

//获取将/帅棋子
function getJiang(state, color){
    for (let x = 3; x <= 5; x++) {
        if(color == COLOR_BLACK){
            for (let y = 0; y <= 2; y++) {
                let piece = state.pieces[x][y];
                if(piece && piece.text == '将')return piece;
            }
        }else{
            for (let y = 7; y <= 9; y++) {
                let piece = state.pieces[x][y];
                if(piece && piece.text == '帥')return piece;
            }
        }
    }
    throw new Error("jiang not found");
}

//获取指定位置的棋子 {canMove, text, color, x, y}
function getPiece(state, bx, by){
    return state.pieces[bx][by];
}

//有自己的棋子
function hasSelfPiece(state, bx, by, step){
    let piece = getPiece(state, bx, by);
    if(piece && piece.color == step)return true;
    return false;
}

//有敌方的棋子
function hasEnemyPiece(state, bx, by, step){
    let piece = getPiece(state, bx, by);
    if(piece && piece.color != step)return true;
    return false;
}

//某子被吃，删除
function deletePiece(state, bx, by){    
    state.pieces[bx][by] = undefined;
}

//移动棋子
function movePiece(state, p1, p2){
    let piece = getPiece(state, p1.x, p1.y);
    let eated = getPiece(state, p2.x, p2.y);
    addUndoRecord(state, piece, p1.x, p1.y, eated);
    deletePiece(state, p1.x, p1.y);
    deletePiece(state, p2.x, p2.y);
    piece.x = p2.x;
    piece.y = p2.y;
    state.pieces[p2.x][p2.y] = piece;
    console.log(`${piece.text} ${p1.x},${p1.y} -> ${p2.x},${p2.y} `);
}

//添加走子记录
function addUndoRecord(state, piece, x, y, eated){
    let record = {};
    record.piece = piece;
    record.origin = {x:x,y:y};
    record.eated = eated;
    state.undos.push(record);
    if(state.undos.length > 5){
        state.undos.shift();
    }
}

//撤销（悔棋）
function undoMove(state){    
    if(!state.undos.length)return;
    console.log("undo");
    let record = state.undos[state.undos.length - 1];
    let piece = record.piece;
    let eated = record.eated;
    let origin = record.origin;
    state.pieces[piece.x][piece.y] = eated;
    state.pieces[origin.x][origin.y] = piece;
    piece.x = origin.x;
    piece.y = origin.y;
    state.step = state.step == COLOR_BLACK?COLOR_RED:COLOR_BLACK;
    state.undos.splice(state.undos.length - 1);
}


//遍历棋子 cb(state, piece) callback返回true则停止遍历
function forEachPiece(state, cb){
    for (let x = 0; x <= 8; x++) {
        for (let y = 0; y <= 9; y++) {
            let piece = state.pieces[x][y];
            if(piece){
                if(cb(state, piece)){
                    return false;
                }
            }
        }        
    }
}


//返回一条线上两个棋子之间的棋子个数
function countPieceInLine(state, p1, p2){
    let count = 0;
    if(p1.x == p2.x){
        let y1 = Math.min(p1.y, p2.y);
        let y2 = Math.max(p1.y, p2.y);
        for (let y = y1 + 1; y < y2; y++) {
            if(getPiece(state, p1.x, y)){
                count ++;
            }
        }
    }else{
        let x1 = Math.min(p1.x, p2.x);
        let x2 = Math.max(p1.x, p2.x);
        for (let x = x1 + 1; x < x2; x++) {
            if(getPiece(state, x, p1.y)){
                count ++;
            }
        }
    } 
    return count;
}

//---------------- 走子规则，可以走返回true --------------------

//将
function jiangCanMove(state, p1, p2){
    if(p1.x != p2.x && p1.y != p2.y)return false;
    if(p2.x < 3 || p2.x > 5)return false;
    if(p2.y > 2 && p2.y < 7)return false;
    if(p1.x == p2.x){
        if(Math.abs(p1.y - p2.y) != 1)return false;
    }else{
        if(Math.abs(p1.x - p2.x) != 1)return false;
    }
    return true;
}

//士
function shiCanMove(state, p1, p2){
    if(p1.x == p2.x || p1.y == p2.y)return false;
    if(p2.x < 3 || p2.x > 5)return false;
    if(p2.y > 2 && p2.y < 7)return false;
    if(Math.abs(p1.x - p2.x) != 1)return false;
    if(Math.abs(p1.y - p2.y) != 1)return false;
    return true;
}

//象
function xiangCanMove(state, p1, p2){
    if(p1.x == p2.x || p1.y == p2.y)return false;    
    if(Math.abs(p1.x - p2.x) != 2)return false;
    if(Math.abs(p1.y - p2.y) != 2)return false;
    if(p1.y < 5 && p2.y > 5)return false;
    if(p1.y > 5 && p2.y < 5)return false;
    if(getPiece(state, (p1.x + p2.x) / 2, (p1.y + p2.y) / 2))return false; 
    return true;
}

//车
function juCanMove(state, p1, p2){
    if(p1.x != p2.x && p1.y != p2.y)return false;    
    if(countPieceInLine(state, p1, p2))return false;
    return true;
}

//马
function maCanMove(state, p1, p2){
    let dx = Math.abs(p1.x - p2.x);
    let dy = Math.abs(p1.y - p2.y);
    if(!((dx == 1 && dy == 2) || (dx == 2 && dy == 1)))return false;
    if(dx == 1){
        if(getPiece(state, p1.x, (p1.y + p2.y) / 2))return false;
    }else{
        if(getPiece(state, (p1.x + p2.x) / 2, p1.y))return false;
    }
    return true;
}

//炮
function paoCanMove(state, p1, p2, step){
    if(p1.x != p2.x && p1.y != p2.y)return false;
    if(hasEnemyPiece(state, p2.x, p2.y, step)){ //吃子
        if(countPieceInLine(state, p1, p2) != 1)return false;
    }else{ //走子
        if(countPieceInLine(state, p1, p2))return false;
    }
    return true;
}

//兵
function bingCanMove(state, p1, p2, step){
    if(p1.x != p2.x && p1.y != p2.y)return false;  
    if(p1.x == p2.x){
        if(Math.abs(p1.y - p2.y) != 1)return false;
    } else {
        if(Math.abs(p1.x - p2.x) != 1)return false;
    }    
    if(step == COLOR_BLACK){
        if(p2.y < p1.y)return false;
        if(p1.y <= 4 && p1.x != p2.x)return false;
    }else{
        if(p2.y > p1.y)return false;
        if(p1.y >= 5 && p1.x != p2.x)return false;        
    }
    return true;
}

//是否将帅碰面
function isJiangFace(state){
    let piece1 = getJiang(state, COLOR_BLACK);
    let piece2 = getJiang(state, COLOR_RED);
    if(piece1.x != piece2.x)return false;
    let count = countPieceInLine(state, piece1, piece2);
    if(count == 0)return true;
    return false;
}

//检查是否将军状态 
function checkJiangJun(state){
    let jiangjun = {};  
    let shuai = getJiang(state, COLOR_RED);
    let jiang = getJiang(state, COLOR_BLACK);
    forEachPiece(state, function(state, piece){
        //任意一个棋子能走到对方将的位置，即将军
        if(piece.color == COLOR_RED){
            if(canMove(state, piece, jiang, COLOR_RED)){
                jiangjun[COLOR_RED] = true;
            }
        }else{
            if(canMove(state, piece, shuai, COLOR_BLACK)){
                jiangjun[COLOR_BLACK] = true;
            }            
        }
    });
    return jiangjun;
}

//通用走子规则
function canMove(state, piece, p2, step){
    if(!piece)return false;
    if(step != piece.color)return false;    
    if(!isInBoardRange(p2.x, p2.y))return false;
    if(piece.x == p2.x && piece.y == p2.y)return false; 
    let piece2 = getPiece(state, p2.x, p2.y);
    if(piece2 && piece2.color == step)return false;
    if(!piece.canMove(state, piece, p2, step))return false; 
    return true;
}

//走子
function doMove(state, p1, p2){
    if(!isInBoardRange(p1.x, p1.y))return false;
    let piece = getPiece(state, p1.x, p1.y);
    if(!canMove(state, piece, p2, state.step))return;    
    movePiece(state, p1, p2); //走子
    state.step = state.step == COLOR_RED? COLOR_BLACK: COLOR_RED;
    if(isJiangFace(state)){  //走子后导致将帅碰面，不能走        
        console.log("can not jiang face");
        undoMove(state);
        return;
    }  
    let jiangjun = checkJiangJun(state);
    if(jiangjun[state.step]){ //走子后处于被将军状态，不能走 
        console.log("can not keep jiang jun");
        undoMove(state);
        return;        
    }
    if(jiangjun[state.step == COLOR_RED? COLOR_BLACK: COLOR_RED]){ //走子后形成了将军
        console.log("jiang jun");  
        playSound('jiang');
    } else {
        playSound('move');
    }
}soud


//播放音效 jiang=将军 move=走子
function playSound(name){
    if(name == 'jiang'){
        audio_jiangjun.play();
    }else{
        audio_chess.play();
    }
}

//坐标是否棋盘之内
function isInBoardRange(bx, by){
    if(bx < 0 || bx > 8)return false;
    if(by < 0 || by > 9)return false;
    return true;
}

//棋盘坐标系转画布坐标系（画布中心为0,0）
function posBoardToCanvas(state, x, y){
    let pos = {};
    pos.x = x * state.blockWidth - state.blockWidth * 4;
    pos.y = y * state.blockWidth - state.blockWidth * 4.5;
    return pos;
}

//窗口坐标系转画布坐标系（画布中心为0,0）
function posWinToCanvas(state, x, y){
    let pos = {};
    if(!state.rotate){
        pos.x = x - state.canvas.width / 2;
        pos.y = y - state.canvas.height / 2;
    }else{  //y越大x越大 x越小y越大      
        pos.x = y - state.canvas.height / 2;
        pos.y = state.canvas.width / 2 - x;        
    }
    return pos;
}

//窗口坐标系转棋盘坐标系
function posWinToBoard(state, x, y){
    let wpos = posWinToCanvas(state, x, y);
    let bpos = {};
    bpos.x = Math.round((wpos.x + state.blockWidth * 4) / state.blockWidth);    
    bpos.y = Math.round((wpos.y + state.blockWidth * 4.5) / state.blockWidth);
    return bpos;    
}

//窗口坐标在棋盘外 
function checkWinOutBoard(state, x, y){
    let cpos = posWinToCanvas(state, x, y);
    if(cpos.x < 0 && cpos.x < (0 - state.board.width / 2)){
        return -1;
    }
    if(cpos.y < 0 && cpos.y < (0 - state.board.height / 2)){
        return -2;
    }
    if(cpos.x > 0 && cpos.x > (state.board.width / 2)){
        return 1;
    }
    if(cpos.y > 0 && cpos.y > (state.board.height / 2)){
        return 2;
    }   
    return 0;     
}

//按窗口坐标画一个棋子
function renderOnePieceByWindow(state, wx, wy, text, red){
    let cpos = posWinToCanvas(state, wx, wy);
    state.ctx.translate(cpos.x, cpos.y);
    renderPieceBase(state.ctx, state.radius, text, red);
    state.ctx.translate(0-cpos.x, 0-cpos.y);
}

//按棋盘坐标画一个棋子
function renderOnePiece(state, piece){
    if(state.moving){
        if(state.moving.piece.x == piece.x && state.moving.piece.y == piece.y)return;
    }
    let cpos = posBoardToCanvas(state, piece.x, piece.y);    
    state.ctx.translate(cpos.x, cpos.y);
    renderPieceBase(state.ctx, state.radius, piece.text, piece.color == COLOR_RED);
    state.ctx.translate(0-cpos.x, 0-cpos.y);
}

//画棋子(中点坐标为0,0)
function renderPieceBase(ctx, radius, text, red){
    let x = 0;
    let y = 0;
    //外圈
    let r2 = radius * 0.95;
    let w2 = radius * 0.15;  
    ctx.fillStyle = "#f3cd91";
    ctx.shadowBlur = radius * 0.4;
    ctx.shadowColor = "black";
    ctx.beginPath();
    ctx.arc(x,y,r2,0,2*Math.PI);
    ctx.fill();
    ctx.shadowBlur = 0;
    //主背景
    let r1 = radius * 0.9;
    ctx.fillStyle = "#feecc5";
    ctx.beginPath();
    ctx.arc(x,y,r1,0,2*Math.PI);
    ctx.fill(); 
    //内圈凹槽
    let r3 = radius * 0.80;
    let w3 = radius * 0.03;    
    ctx.strokeStyle = "#bd924a";
    ctx.lineWidth = w3;
    ctx.beginPath();
    ctx.arc(x,y,r3,0,2*Math.PI);
    ctx.stroke();

    //字
    let fz = Math.round(radius * 1.35);
    ctx.fillStyle = red?"#df0000":"#000000";
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.font = fz + 'px 楷体';
    if(!red){
        ctx.rotate(Math.PI);
    }
    ctx.fillText(text, x, y);  
    if(!red){
        ctx.rotate(0 - Math.PI);
    }
} 


//画棋盘(棋盘中点坐标为0,0) 一格宽度, 格子到边缘的距离)
function renderBoard(ctx, blockWidth, padding){
    let totalW = blockWidth * 8 + padding * 2;
    let totalH = blockWidth * 9 + padding * 2;
    let p = {};
    let x = 0 - totalW / 2;
    let y = 0 - totalH / 2;
    //背景
    ctx.fillStyle = "#EBBE82";
    ctx.fillRect(x, y, totalW, totalH); 
    //边框线
    ctx.strokeStyle = "#000000";
    ctx.lineCap = "square";
    ctx.lineWidth = 3;
    ctx.beginPath();
    ctx.rect(x + padding, y + padding, blockWidth*8, blockWidth*9);
    ctx.stroke();
    //横线
    ctx.lineWidth = 1;
    for (let i = 1; i < 9; i++) {   
        let ty = y + padding + i * blockWidth     
        ctx.beginPath();
        ctx.moveTo(x + padding, ty);
        ctx.lineTo(x + padding + blockWidth*8, ty);
        ctx.stroke();
    }
    //竖线
    for (let i = 1; i < 8; i++) {   
        let tx = x + padding + i * blockWidth     
        ctx.beginPath();
        ctx.moveTo(tx, y + padding);
        ctx.lineTo(tx, y + padding + blockWidth * 4);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo(tx, y + padding + blockWidth * 5);
        ctx.lineTo(tx, y + padding + blockWidth * 9);
        ctx.stroke();        
    }  
    //九宫格斜线
    ctx.beginPath();
    ctx.moveTo(x + padding + blockWidth * 3, y + padding);
    ctx.lineTo(x + padding + blockWidth * 5, y + padding + blockWidth * 2);
    ctx.stroke();  
    ctx.beginPath();
    ctx.moveTo(x + padding + blockWidth * 3, y + padding + blockWidth * 7);
    ctx.lineTo(x + padding + blockWidth * 5, y + padding + blockWidth * 9);
    ctx.stroke();   
    ctx.beginPath();
    ctx.moveTo(x + padding + blockWidth * 5, y + padding);
    ctx.lineTo(x + padding + blockWidth * 3, y + padding + blockWidth * 2);
    ctx.stroke();             
    ctx.beginPath();
    ctx.moveTo(x + padding + blockWidth * 5, y + padding + blockWidth * 7);
    ctx.lineTo(x + padding + blockWidth * 3, y + padding + blockWidth * 9);
    ctx.stroke();
}

//按canvas大小，计算棋盘位置等
function updateSize(state){
    let width = state.canvas.width;
    let height = state.canvas.height;    
    state.board = {};
    if(width > height){
        state.rotate = true; 
        width = state.canvas.height;
        height = state.canvas.width; 
    }else{
        state.rotate = false; 
    }
    let blockWidth1 = width / 9.3;
    let blockWidth2 = height / 10.3;
    if(blockWidth1 > blockWidth2){ //更宽一点，左右留空
        state.blockWidth = Math.floor(blockWidth2);
        let padding = (height - state.blockWidth * 9) / 2;
        state.board.x = padding;
        state.board.y = 0;
        state.padding = padding;        
    }else{ //更高一点，上下留空
        state.blockWidth = Math.floor(blockWidth1);
        let padding = (width - state.blockWidth * 8) / 2;
        state.board.x = 0;
        state.board.y = padding;
        state.padding = padding;
    } 
    state.radius = state.blockWidth * 0.45;
    state.board.width = state.blockWidth * 8 + state.padding * 2;
    state.board.height = state.blockWidth * 9 + state.padding * 2;
    state.edge = {};    
    state.ctx.translate(state.canvas.width / 2,state.canvas.height / 2);
    if(state.rotate){        
        state.ctx.rotate(90 * Math.PI/180); 
        state.edge.x = (state.canvas.height - state.board.width) / 2;        
        state.edge.y = (state.canvas.width - state.board.height) / 2;
        state.width = state.canvas.height;
        state.height = state.canvas.width;
    }else{
        state.edge.x = (state.canvas.width - state.board.width) / 2;
        state.edge.y = (state.canvas.height - state.board.height) / 2;
        state.width = state.canvas.width;
        state.height = state.canvas.height;        
    }
}

//绘制允许走子的点
function renderTargetPoint(state){
    if(state.select){
        let piece = getPiece(state, state.select.x, state.select.y)
        for (let x = 0; x <= 8; x++) {
            for (let y = 0; y <= 9; y++) { 
                if(canMove(state, piece, {x:x,y:y}, state.step)){
                    let cpos = posBoardToCanvas(state, x, y);
                    state.ctx.fillStyle = "#00ff00"
                    state.ctx.fillRect(cpos.x, cpos.y, 3, 3);
                }
            }
        }
    }
}

//绘制关闭中区域
function renderClosingEdge(state){
    let rc = {};
    let ctx = state.ctx; 
    if(!state.closing)  return; 
    ctx.fillStyle = "#000000";
    let p1 = posWinToCanvas(state, state.closing.src.x, state.closing.src.y);
    let p2 = posWinToCanvas(state, state.closing.x, state.closing.y);
        
    if(state.closing.edge == -1){
        rc.x = 0 - state.board.width / 2;
        if(p2.x <= rc.x)return;
        rc.d = p2.x - rc.x;
        rc.y = 0 - state.board.height / 2 - state.edge.y;
        ctx.fillRect(rc.x, rc.y, rc.d, state.height);
    }else if(state.closing.edge == 1){  
        rc.x = state.board.width / 2;
        if(p2.x >= rc.x)return;
        rc.d = rc.x - p2.x;
        rc.y = 0 - state.board.height / 2 - state.edge.y;
        ctx.fillRect(rc.x - rc.d, rc.y, rc.d, state.height);        
    }else if(state.closing.edge == -2){        
        rc.y = 0 - state.board.height / 2;
        if(p2.y <= rc.y)return;
        rc.d = p2.y - rc.y;
        rc.x = 0 - state.board.width / 2;       
        ctx.fillRect(rc.x, rc.y, state.width, rc.d);        
    }else if(state.closing.edge == 2){        
        rc.y = state.board.height / 2
        if(p2.y >= rc.y)return;
        rc.d = rc.y - p2.y;
        rc.x = 0 - state.board.width / 2;        
        ctx.fillRect(rc.x, rc.y - rc.d, state.width, rc.d);        
    }

}


//绘制边缘区域
function renderEdge(state){
    let rc = {};
    let ctx = state.ctx;    
    ctx.fillStyle = "#000000"
    //-x方向
    rc.x = 0 - state.board.width / 2 - state.edge.x;
    rc.y = 0 - state.board.height / 2 - state.edge.y;
    rc.d = state.edge.x;
    ctx.fillRect(rc.x, rc.y, state.edge.x, state.height);
    //x方向    
    rc.x = state.board.width / 2;
    rc.y = 0 - state.board.height / 2 - state.edge.y;
    ctx.fillRect(rc.x, rc.y, state.edge.x, state.height);
    //-y方向    
    rc.x = 0 - state.board.width / 2 - state.edge.x;
    rc.y = 0 - state.board.height / 2 - state.edge.y;
    ctx.fillRect(rc.x, rc.y, state.width, state.edge.y);
    //y方向    
    rc.x = 0 - state.board.width / 2 - state.edge.x;
    rc.y = state.board.height / 2;
    ctx.fillRect(rc.x, rc.y, state.width, state.edge.y);

}

//全部绘制
function render(state){
    renderBoard(state.ctx, state.blockWidth, state.padding);
    forEachPiece(state, renderOnePiece);
    //renderTargetPoint(state);
    renderEdge(state);
    if(state.closing){
        renderClosingEdge(state);
    }    
    if(state.moving){        
        renderOnePieceByWindow(state, state.moving.x, state.moving.y, state.moving.piece.text, state.moving.piece.color == COLOR_RED);
    }
}


function onMouseDown(state, x, y){
    let edge = checkWinOutBoard(state, x, y);
    if(edge){
        state.closing = {};
        state.closing.edge = edge;      
        state.closing.src = {};  
        state.closing.src.x = x;
        state.closing.src.y = y;
        state.closing.x = x;
        state.closing.y = y;
        return;        
    }
    let bpos = posWinToBoard(state, x, y);
    let piece = getPiece(state, bpos.x, bpos.y);    
    if(!piece){  
        //state.select = undefined;
        return;
    }
    state.select = {};
    state.select.x = bpos.x;
    state.select.y = bpos.y;
    state.moving = {};
    state.moving.x = x;
    state.moving.y = y;
    state.moving.piece = piece;

}

function onMouseMove(state, x, y){
    if(state.moving){
        state.moving.x = x;
        state.moving.y = y;
        render(state);
        return;
    }
    if(state.closing){
        state.closing.x = x;
        state.closing.y = y;
        render(state);
    }
}

//判断是否需要重开
function checkReset(state, x, y){
    if(checkWinOutBoard(state, x, y)){
        if(state.moving.piece.text == '将' || state.moving.piece.text == '帥'){
            return true;
        }
    }
    return false;
}

//判断是否为悔棋
function checkUndo(state, x, y){
    let undos = state.undos;
    if(undos.length <= 0)return false;
    let frompos = state.select;
    let topos = posWinToBoard(state, x, y);
    let record = undos[undos.length - 1];
    let rtopos = record.piece;
    let rfrompos = record.origin;
    if(frompos.x == rtopos.x && frompos.y == rtopos.y){
        if(topos.x == rfrompos.x && topos.y == rfrompos.y){
            return true;
        }
    }
    return false;
}

//关闭
function doClose(state){
    initPieces(state);            
    render(state);
    window.close();
}

function onMouseUp(state, x, y){
    let update = false;
    if(state.closing){
        let edge = checkWinOutBoard(state, x, y);
        if((0 - edge) == state.closing.edge){
            doClose(state);
            return;
        }
        state.closing = undefined;
        update = true;
    }
    if(state.moving){ 
        if(checkReset(state,x,y)){ //重开判断
            initPieces(state);            
            render(state);
            return;
        }
        state.moving = undefined;      
        update = true;
    }
    if(state.select){
        if(checkUndo(state, x, y)){ //悔棋判断
            undoMove(state);
        }else{ //走子
            let p2 = posWinToBoard(state, x, y);
            doMove(state, state.select, p2);                    
        }
        update = true;
    }
    if(update)render(state);
}

function setEventListener(state, div){
    let opt = {passive: false};
    
    div.addEventListener('mousedown', (ev)=>{
        onMouseDown(state, ev.x, ev.y);
    });
    div.addEventListener('mouseup', (ev)=>{
        onMouseUp(state, ev.x, ev.y);
    });  
    div.addEventListener('mousemove', (ev)=>{
        onMouseMove(state, ev.x, ev.y);
    }); 
    div.addEventListener('mouseleave', (ev)=>{
        onMouseUp(state, -1, -1);
    });
    div.addEventListener('touchstart', (ev)=>{
        let x = ev.touches[0].pageX;
        let y = ev.touches[0].pageY; 
        onMouseDown(state, x, y); 
        ev.preventDefault();       
    },opt);
    div.addEventListener('touchmove', (ev)=>{
        let x = ev.touches[0].pageX;
        let y = ev.touches[0].pageY; 
        onMouseMove(state, x, y);
        ev.preventDefault();  
    },opt);
    div.addEventListener('touchend', (ev)=>{        
        let x = ev.changedTouches[0].pageX;
        let y = ev.changedTouches[0].pageY;        
        onMouseUp(state, x, y); 
        ev.preventDefault(); 
    },opt);
    div.addEventListener('touchcancel', (ev)=>{
        onMouseUp(state, -1, -1);
        ev.preventDefault(); 
    },opt);    
}



//按body大小创建画布
function createCanvas(){
    let body = container;
    let canvas = document.createElement('canvas'); 
    let rc = body.getBoundingClientRect();
    canvas.width = rc.width;
    canvas.height = rc.height;    
    canvas.style.width = "100%";
    canvas.style.height = "100%";  
    body.innerHTML = '';
    body.appendChild(canvas);
    return canvas;
}

function createState(){
    let state = {};
    state.canvas = createCanvas(); 
    state.ctx = state.canvas.getContext('2d');
    updateSize(state);
    initPieces(state);
    render(state);
    setEventListener(state, container);
    return state;
}

function init(){     
    //disableTouchMove(); 
    
    let state = createState();
    window.onresize = function(){
        state.canvas = createCanvas(); 
        state.ctx = state.canvas.getContext('2d');        
        updateSize(state);
        render(state);
    }
}   

</script>
</head>
<body onload="init()">
<div id="container" class="full" ></div>
<audio id="audio_chess" style="display:none;" src="data:audio/mp3;base64,
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
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqoJZoaIeI34AAAI6ucnA+4bEwEi8VyJf4FY8LrNCsjc
ZDtjf6o6b6WinZkG5ub2bgBpCkDwTEUeYZb4u+TTWq+SwH//////////////4rtL//tQZOQBMk8N
1fnpGxobwAocAAABBlhVT+ekZyAjACiQAAAEXZ3Vk+hMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqC7u6oAAAAAAVoIEYCIm8
QpTi9QRUxMgAACBmQMk8uWWQj//////////////7UGTWAfFrC1J56DMKAKAJ4AAAAQVIZ0fsMEVo
AYBoQBAABP////6aTEFNRTMuOTkuNaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqr/+1Bk04PxTgzR+wwY2gBgGgAEAAECyCFdxTABaE6AZYAQAACqBkQRlUxBTUUzLjk5
LjVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//tQZMGD8JYF1XGp
SAoAYBnQBAABAbAPT8aEwCg7gKXAIAAEVQREFslMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VQjIJqgAMcAwsZfuN//////////////V/f/7UGSrj/AMANICIAAIAGAZ8AQAAQAsA1AGgAAgAYBn
gBAABN3uV6tSTEFNRTMuOTkuNaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqBwHAgFAYAAgDAAAAAABgKgwLQJSEL+eG
+BbPy+b+BCcz/8oGAf/AgYB+gAAAABIRAMBQKBQIBAAAAAAAmgAbeABUFt8FpCe+IICl4CwgQbfx
EkQ+Fbz/+1Bkq4/wDADTAaAACABgGdAEAAEAMANKB4AAIAGAZ0AQAAQVCR71VUxBTUUzLjk5LjVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
TEFNRTMuOTkuNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//tQZLgH8AsA04GgAAgA
YBmgBAABASQPU9TAACBHAKWChAAAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/7UGTXAAFODFDuVKAABiAZ1cCAAAVISz+Y1QAAAAA/wwAA
AFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVX/+xBk3Y/wAAB/hwAACAAAD/DgAAEAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==
" ></audio>
<audio id="audio_jiangjun" style="display:none;" src="data:audio/mp3;base64,
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
Pmay5p9VSDss1im2lLHQPUeVRBISdlKQ6bmCXnyULsLq5zNp2DzcsSM8gmCirPl2jHqu//tQxO0A
CcEnbee08QEopK689p2srMIlveVEMhBrS3SpTSEESGQxrvQ3p5nyiRyK9KoZ6rquiIhjooUYoq8S
5IMnTvMlhCpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
mAyNmagCxN4sAaKJYAcYOFAAJyG0MFBIVipPzMfeSpe71YJ1HF/5Cej9HyHF5izGe/u1CkHUTahP
/4vHfRaMnahQi8llGICVySDRrrDGGGu2AU85yEvBkv/7UMT2AAmlJXPntK1haqSufPaKFKk8I7ou
pJ4mNE9CucTcvzXyoiDPX3AW6eYfhQLUzN//M+6PVlZrp//5/TZE/+qoFy7JTEFNRTMuOTkuNVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVijupqJmdxNrQA6py2EuE
hfhcKOIYJqPskNCqP1tTayyz7iSrhZJtYhVIH09dXrP7nSyl74bklOtTtUa9uEoCbA3xYWILMWDB
Q7xDGPILgABiYJP/+1DE/IAKtSNp55RSIXsibD2HlDydxdUcTVp0kUzqNpiVSg22iUz5Q/sigsta
g8EzwPog2qGaDYTzN9VyvdSar9BWS/o9CkxBTUUzLjk5LjWqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqS4ia
qYYlhsAACc0JCah4Kiu4p8iFNE+rpOuqaYacN1ON9rIADsSrcFyxUtbIol7KHlyK//tQxOcACQyf
X+e8QeEGIes884mlBdYd5ljCMyBJyJwjSfWJopspWr6qZPxVXZRnjOooH87l5SvRgi2oh0cZd062
ppzDB8tMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
WCdXZniPhEAAAuceSlIj3yJwz1L+VXQIiyOKQv/7UMTkgAjUdVvnpasg9xapfPWJ3MEd6lP6L9Xn
OkmWQ1/vaSSDQkgYAyxfQ7hLPgyeX3+2aF+CpWlCTNIQKzvn/bY4l8sqTEFNRTMuOTkuNaqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqr/+1DE1IBGaH9R54jRIMaT6Py3lG2qqqqqqqqqqqqqqqqqqqqqqqqqqqqqhYBAANgB
8JX8Xg+L7//5yeXqXwfq3MPK//8S1UxBTUUzLjk5LjVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuOTku
NVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//tQxMkARUB1R+Yd
p6CMj+b08SigVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVUxBTUUzLjk5LjVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/7UMSwAkMEdTNmAERgDAAlwAAAAFVVVVVVVVVVTEFN
RTMuOTkuNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVX/+xDE1gPAAAH+AAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/7EMTW
A8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sQxNYDwAABpAAAACAA
ADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==

" ></audio>

</body>
</html>
